package com.example.file_explorer.utils;

import com.example.file_explorer.entity.FileNode;
import com.example.file_explorer.result.Code;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j;
import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;

@Log4j
public class IOUtils {

    private static FileNode root = null;

    /**
     * 启动时自动生成树
     */
    static {
        try {
            initFileTree();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /** 获取文件夹下所有的文件信息
     *
     * @param path 路径名称
     * @return 该文件夹下所有文件
     */

    public static Set<FileNode> getFileList(String path) throws IOException {
        File file = new File(path);
        File[] fs = file.listFiles();
        Set<FileNode> res = new HashSet<>();
        if (fs == null) {
            return null;
        }
        for (File f : fs) {
            Path testPath = Paths.get(f.getPath());
            BasicFileAttributeView basicView = Files.getFileAttributeView(testPath, BasicFileAttributeView.class);
            BasicFileAttributes basicFileAttributes = basicView.readAttributes();
            FileNode fileNode = new FileNode(f.getName(), basicFileAttributes.size(), basicFileAttributes.creationTime().toMillis(),
                    basicFileAttributes.lastAccessTime().toMillis(), basicFileAttributes.lastModifiedTime().toMillis(), new HashSet<>(), f.isDirectory());
            res.add(fileNode);
        }
        return res;
    }

    /** 初始化所有文件节点
     *
     * @return 根节点
     * @throws IOException
     */
    public static FileNode initFileTree() throws IOException {
        root = new FileNode("../root", null, null, null, null, getFileList("../root"), true);
        dfs("../root", root);
        return root;
    }
    private static void dfs(String path, FileNode lastNode) throws IOException{
        for (FileNode node : lastNode.getChildren()) {
            Set<FileNode> fileList = getFileList(path + "/" + node.getFileName());
            if (fileList != null) {
                node.setChildren(fileList);
                dfs(path + "/" + node.getFileName(), node);
            }
        }
    }

    /** 根据路径返回子节点（文件）
     *
     * @param path
     * @return
     */
    public static Set<FileNode> directory(String path) {
        if (!path.isEmpty() && path.charAt(0) == '/') {
            path = path.substring(1);
        }
        Set<FileNode> set = root.getChildren();
        String[] paths = path.split("/");
        Set<FileNode> temp;
        if (paths.length == 0 || !paths[0].isEmpty()) { // 防止传入空字符串分隔
            for (String filename : paths) {
                temp = null;
                for (FileNode node : set) {
                    if (node.getFileName().equals(filename)) {
                        temp = node.getChildren();
                        break;
                    }
                }
                if (temp == null) {
                    return null;
                }
                set = temp;
            }
        }
        return set;
    }

    public static Set<FileNode> directoryNoChildren(String path) {
        Set<FileNode> set = directory(path);
        Set<FileNode> res = new HashSet<>();
        if (set == null) {
            return null;
        }
        for (FileNode node : set) {
            FileNode file = new FileNode(node);
            res.add(file);
        }
        return res;
    }

    public static boolean addDirectory(String path, String name){
        Set<FileNode> fileList = directory(path);
        File file = new File("../root/" + path + "/" + name);
        long now = System.currentTimeMillis();
        boolean isSucceed = file.mkdir();
        if (isSucceed && fileList != null) {
            fileList.add(new FileNode(name, (long)0, now, now, now, new HashSet<>(), file.isDirectory()));
        }
        return isSucceed;
    }

    /**
     * 文件下载
     * @param path 文件所在文件夹路径
     * @param fileName 文件名
     * @param resp 响应体
     * @return
     * @throws IOException
     */
    public static Code download(String path, String fileName, HttpServletResponse resp) throws IOException{
        File file = new File("../root/" + path +'/'+ fileName);
        if(!file.exists()){
            return Code.NOT_FOUND_FILE;
        }
        resp.reset();
        resp.setContentType("application/octet-stream");
        resp.setCharacterEncoding("utf-8");
        resp.setContentLength((int) file.length());
        resp.setHeader("Content-Disposition", "attachment;filename=" + fileName );

        try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));) {
            byte[] buff = new byte[1024];
            OutputStream os  = resp.getOutputStream();
            int i = 0;
            while ((i = bis.read(buff)) != -1) {
                os.write(buff, 0, i);
                os.flush();
            }
        } catch (IOException e) {
            log.error("{}",e);
            return Code.FAIL_DOWNLOAD_FILE;
        }
        return Code.SUCCESS;
    }

    /**
     * 删除文件
     * @param path 文件所在文件夹路径
     * @param fileName 文件名
     * @return
     */
    public static Code deleteFile(String path, String fileName) {
        File file = new File("../root/" + path +'/'+ fileName);
        if(!file.exists()){
            return Code.NOT_FOUND_FILE;
        }
        Set<FileNode> directory = directory(path);
        if (directory == null) {
            return Code.FAIL_DELETE_FILE;
        }
        if (file.delete()) {
            directory.removeIf(node -> node.getFileName().equals(fileName));
            return Code.SUCCESS;
        }else {
            return Code.FAIL_DELETE_FILE;
        }
    }

    /**
     * 重命名文件
     * @param path 文件所在文件夹路径
     * @param fileName 文件名
     * @param newName 新的文件名
     * @return
     */
    public static Code fileRename(String path, String fileName, String newName) {
        File file = new File("../root/" + path +'/'+ fileName);
        if(!file.exists()){
            return Code.NOT_FOUND_FILE;
        }
        File newFile = new File("../root/" + path +'/'+ newName);
        if (newFile.exists()) {
            return Code.FILE_EXIST;
        }
        if (file.renameTo(newFile)) {
            Set<FileNode> directory = directory(path);
            if (directory == null) {
                return Code.FAIL_DELETE_FILE;
            }
            for (FileNode node : directory) {
                if (node.getFileName().equals(fileName)) {
                    node.setFileName(newName);
                }
            }
            return Code.SUCCESS;
        }else {
            return Code.FAIL_RENAME_FILE;
        }
    }

    /** 文件上传
     *
     * @param path 上传的文件保存路径
     * @param multipartFile 文件输入流
     * @return 结果
     * @throws IOException
     */
//    public static Code fileUpload(String path, MultipartFile multipartFile) throws IOException {
//        //TODO 检查操作从磁盘改为对树操作
//        String filename = multipartFile.getOriginalFilename(); // 获取文件名称
//        File directory = new File("../root" + path);
//        Set<FileNode> directory1 = directory(path);
//        if (!directory.exists() || directory1 == null) {
//            // 未找到文件夹
//            return Code.NOT_FOUND_DIRECTORY;
//        }
//        File newFile = new File("../root" + path + "/" + filename);
//        if (newFile.exists()) {
//            // 检查此文件夹下有无同名文件
//            return Code.FILE_EXIST;
//        }
//        FileUtils.writeByteArrayToFile(newFile, multipartFile.getBytes()); // 创建文件，实现文件上传
//        // 新文件挂载在树中
//        directory1.add(new FileNode(filename, multipartFile.getSize(), System.currentTimeMillis(), System.currentTimeMillis(), System.currentTimeMillis(), new HashSet<>()));
//        return Code.SUCCESS;
//    }

    public static Code fileUpload(String path, MultipartFile multipartFile) throws IOException {
        InputStream inputStream = multipartFile.getResource().getInputStream();
        //TODO 检查操作从磁盘改为对树操作
        String filename = multipartFile.getOriginalFilename(); // 获取文件名称
        File directory = new File("../root" + path);
        Set<FileNode> directory1 = directory(path);
        if (!directory.exists() || directory1 == null) {
            // 未找到文件夹
            return Code.NOT_FOUND_DIRECTORY;
        }
        File newFile = new File("../root" + path + "/" + filename);
        if (newFile.exists()) {
            // 检查此文件夹下有无同名文件
            return Code.FILE_EXIST;
        }
        try (OutputStream os = new FileOutputStream(newFile)) {
            // 创建文件，实现文件上传
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(bytes)) != -1) {
                os.write(bytes, 0, len);
            }
        } catch (IOException e) {
            log.error("{}", e);
            return Code.FAIL_DOWNLOAD_FILE;
        }
        // 新文件挂载在树中
        directory1.add(new FileNode(filename, multipartFile.getSize(), System.currentTimeMillis(), System.currentTimeMillis(), System.currentTimeMillis(), new HashSet<>(), false));
        return Code.SUCCESS;
    }

    public static Code move(String src, String desc, String filename) {
        return null;
    }

    /** 复制文件
     *
     * @param src 源路径
     * @param desc 目标路径
     * @param filename 文件名
     * @return 结果
     * @throws IOException
     */
    public static Code copy(String src, String desc, String filename) throws IOException {
        File srcFile = new File("../root" + src + "/" + filename);
        if (!srcFile.exists()) {
            return Code.NOT_FOUND_FILE;
        }
        File descFile = new File("../root" + desc + "/" + filename);
        // 调用工具类，移动文件
        if (descFile.exists()) {
            return Code.FILE_EXIST; // 目标文件已存在
        }
        if (srcFile.isFile()) {
            FileUtils.copyFile(srcFile, descFile);
        }else if (srcFile.isDirectory()) {
            FileUtils.copyDirectory(srcFile, descFile);
        }
        Set<FileNode> srcDirectory = directory(src);
        Set<FileNode> descDirectory = directory(desc);
        if (srcDirectory == null || descDirectory == null) {
            return Code.NOT_FOUND_DIRECTORY;
        }
        // 获取到节点相关信息，复制到desc目录下
        FileNode tempNode = null;
        for (FileNode node : srcDirectory) {
            if (node.getFileName().equals(filename)) {
                tempNode = node;
                break;
            }
        }
        if (tempNode == null) {
            return Code.NOT_FOUND_FILE;
        }
        descDirectory.add(new FileNode(tempNode));
        return Code.SUCCESS;
    }
}
